<template>
  <div class="file-upload-container">
    <a-upload
      :file-list="fileList"
      :action="uploadUrl"
      :headers="headers"
      :before-upload="beforeUpload"
      :custom-request="customUploadRequest"
      :multiple="multiple"
      :disabled="disabled"
      :max-count="maxCount"
      :show-upload-list="showUploadList"
      :accept="accept"
      :list-type="listType"
      @change="handleChange"
      @preview="handlePreview"
      @remove="handleRemove"
    >
      <div v-if="listType === 'picture-card'" class="upload-btn">
        <plus-outlined />
        <div class="upload-text">{{ t('file.upload') }}</div>
      </div>
      <a-button v-else :disabled="disabled" type="primary">
        <upload-outlined />
        {{ t('file.upload') }}
      </a-button>
      <template #itemRender="{ file, actions }">
        <slot name="itemRender" :file="file" :actions="actions"></slot>
      </template>
    </a-upload>
    
    <!-- 文件预览模态框 -->
    <a-modal
      v-model:open="previewVisible"
      :footer="null"
      @cancel="handlePreviewCancel"
    >
      <div v-if="previewFile" class="preview-content">
        <!-- 图片预览 -->
        <img
          v-if="isImageFile(previewFile)"
          :src="previewFile.url || previewFile.thumbUrl"
          alt="Preview"
          class="preview-image"
        />
        <!-- PDF预览 -->
        <iframe
          v-else-if="isPdfFile(previewFile)"
          :src="previewFile.url || previewFile.thumbUrl"
          class="preview-pdf"
        ></iframe>
        <!-- 其他文件预览 -->
        <div v-else class="preview-other">
          <file-outlined class="file-icon" />
          <div class="file-name">{{ previewFile.name }}</div>
          <div class="file-size">{{ formatFileSize(previewFile.size) }}</div>
          <a-button type="primary" @click="handleDownload(previewFile)">
            {{ t('file.download') }}
          </a-button>
        </div>
      </div>
    </a-modal>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { message } from 'ant-design-vue'
import { useI18n } from 'vue-i18n'
import type { UploadProps, UploadChangeParam, UploadFile } from 'ant-design-vue'

// 定义组件名称
defineOptions({
  name: 'FileUpload'
})

// 定义组件属性
const props = defineProps({
  // 文件列表
  value: {
    type: Array as () => UploadFile[],
    default: () => []
  },
  // 接受的文件类型
  accept: {
    type: String,
    default: ''
  },
  // 是否多选
  multiple: {
    type: Boolean,
    default: false
  },
  // 上传的最大数量限制
  maxCount: {
    type: Number,
    default: 5
  },
  // 文件大小限制(MB)
  maxSize: {
    type: Number,
    default: 10
  },
  // 上传列表的展示方式
  listType: {
    type: String as () => 'text' | 'picture' | 'picture-card',
    default: 'text'
  },
  // 是否禁用
  disabled: {
    type: Boolean,
    default: false
  },
  // 是否显示上传列表
  showUploadList: {
    type: Boolean,
    default: true
  },
  // 自定义上传URL
  uploadUrl: {
    type: String,
    default: '/api/file/upload'
  },
  // 额外的上传参数
  data: {
    type: Object,
    default: () => ({})
  }
})

// 定义组件事件
const emit = defineEmits<{
  (e: 'update:value', fileList: UploadFile[]): void
  (e: 'success', file: UploadFile): void
  (e: 'error', error: any): void
  (e: 'change', info: UploadChangeParam): void
  (e: 'download', file: UploadFile): void
}>()

// 国际化
const { t } = useI18n()

// 文件列表
const fileList = ref<UploadFile[]>([...props.value])

// 预览相关
const previewVisible = ref(false)
const previewFile = ref<UploadFile | null>(null)

// 临时getToken函数，应替换为实际的token获取方法
const getToken = () => {
  return localStorage.getItem('token') || ''
}

// 上传请求头
const headers = computed(() => {
  return {
    Authorization: `Bearer ${getToken()}`
  }
})

// 监听value变化
watch(() => props.value, (newVal) => {
  fileList.value = [...newVal]
}, { deep: true })

// 监听文件列表变化
watch(fileList, (newVal) => {
  emit('update:value', newVal)
}, { deep: true })

// 上传前检查
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
  // 检查文件大小
  const isLimitSize = file.size / 1024 / 1024 < props.maxSize
  if (!isLimitSize) {
    message.error(t('file.sizeLimit', { size: props.maxSize }))
    return false
  }
  
  // 检查文件类型
  if (props.accept && props.accept !== '*') {
    const acceptTypes = props.accept.split(',')
    const fileType = file.type || getFileTypeByName(file.name)
    const isAcceptType = acceptTypes.some(type => {
      if (type.startsWith('.')) {
        // 检查后缀
        return file.name.toLowerCase().endsWith(type.toLowerCase())
      } else {
        // 检查MIME类型
        return fileType.includes(type.trim())
      }
    })
    
    if (!isAcceptType) {
      message.error(t('file.typeNotAccepted'))
      return false
    }
  }
  
  return true
}

// 通过文件名获取文件类型
const getFileTypeByName = (fileName: string): string => {
  const ext = fileName.split('.').pop()?.toLowerCase() || ''
  const typeMap: Record<string, string> = {
    jpg: 'image/jpeg',
    jpeg: 'image/jpeg',
    png: 'image/png',
    gif: 'image/gif',
    pdf: 'application/pdf',
    doc: 'application/msword',
    docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    xls: 'application/vnd.ms-excel',
    xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    ppt: 'application/vnd.ms-powerpoint',
    pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
    txt: 'text/plain',
    mp4: 'video/mp4',
    mp3: 'audio/mpeg'
  }
  
  return typeMap[ext] || 'application/octet-stream'
}

// 自定义上传请求
const customUploadRequest: UploadProps['customRequest'] = async (options) => {
  const { file, onProgress, onSuccess, onError } = options
  
  if (!(file instanceof File)) {
    return
  }
  
  try {
    // 显示上传进度
    onProgress && onProgress({ percent: 0 })
    
    // 创建FormData对象
    const formData = new FormData()
    formData.append('file', file)
    
    // 添加额外参数
    if (props.data) {
      Object.entries(props.data).forEach(([key, value]) => {
        formData.append(key, String(value))
      })
    }
    
    // 上传文件
    const res = await fetch(props.uploadUrl, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${getToken()}`
      },
      body: formData
    }).then(response => response.json())
    
    // 上传成功回调
    if (res.success) {
      const fileData = res.data
      const uploadedFile = {
        uid: fileData.id,
        name: fileData.originalName,
        status: 'done',
        url: fileData.url,
        response: res,
        size: fileData.size,
        type: fileData.type || 'application/octet-stream' // 使用type而非fileType
      } as UploadFile
      
      onSuccess && onSuccess(res)
      emit('success', uploadedFile)
    } else {
      onError && onError(new Error(res.message))
      emit('error', res.message)
    }
  } catch (error) {
    console.error('文件上传失败:', error)
    onError && onError(error as Error)
    emit('error', error)
  }
}

// 上传状态改变
const handleChange = (info: UploadChangeParam) => {
  fileList.value = [...info.fileList]
  emit('change', info)
}

// 文件预览
const handlePreview = (file: UploadFile) => {
  previewFile.value = file
  previewVisible.value = true
}

// 关闭预览
const handlePreviewCancel = () => {
  previewVisible.value = false
  previewFile.value = null
}

// 文件移除
const handleRemove = (file: UploadFile) => {
  const index = fileList.value.findIndex(item => item.uid === file.uid)
  if (index !== -1) {
    fileList.value.splice(index, 1)
  }
}

// 下载文件
const handleDownload = (file: UploadFile) => {
  emit('download', file)
  
  if (file.url) {
    const link = document.createElement('a')
    link.href = file.url
    link.target = '_blank'
    link.download = file.name
    link.click()
  }
}

// 判断是否为图片文件
const isImageFile = (file: UploadFile): boolean => {
  return file.type?.startsWith('image/') || /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(file.name)
}

// 判断是否为PDF文件
const isPdfFile = (file: UploadFile): boolean => {
  return file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf')
}

// 格式化文件大小
const formatFileSize = (size?: number): string => {
  if (!size) return '0 B'
  
  const units = ['B', 'KB', 'MB', 'GB', 'TB']
  let index = 0
  let formattedSize = size
  
  while (formattedSize >= 1024 && index < units.length - 1) {
    formattedSize /= 1024
    index++
  }
  
  return `${formattedSize.toFixed(2)} ${units[index]}`
}

// 暴露公共方法
defineExpose({
  fileList,
  resetUpload: () => {
    fileList.value = []
  }
})
</script>

<style lang="scss" scoped>
.file-upload-container {
  .upload-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    
    .anticon {
      font-size: 24px;
      color: $text-color-secondary;
    }
    
    .upload-text {
      margin-top: $spacing-xs;
      color: $text-color-secondary;
    }
  }
  
  .preview-content {
    display: flex;
    justify-content: center;
    align-items: center;
    
    .preview-image {
      max-width: 100%;
      max-height: 500px;
    }
    
    .preview-pdf {
      width: 100%;
      height: 500px;
      border: none;
    }
    
    .preview-other {
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: $spacing-lg;
      
      .file-icon {
        font-size: 48px;
        color: $primary-color;
        margin-bottom: $spacing-md;
      }
      
      .file-name {
        font-size: $font-size-lg;
        font-weight: 500;
        margin-bottom: $spacing-sm;
        text-align: center;
        word-break: break-all;
      }
      
      .file-size {
        color: $text-color-secondary;
        margin-bottom: $spacing-lg;
      }
    }
  }
}
</style> 